//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <eltypes.h>
#include "marshal.h"
#include "elapi.h"
#ifdef _win32
#include <aura.h>
#endif

ECode Proxy_ProcessMsh_BufferSize(
    /* [in] */ const CIMethodEntry *pMethodInfo,
    /* [in] */ UInt32 *puArgs,
    /* [out] */ UInt32 *puInSize,
    /* [out] */ UInt32 *puOutSize)
{
    int n, cParams;
    UInt32 uInSize, uOutSize, uSize = 0;
    const CIBaseType *pParams;

    uInSize = 0;
    uOutSize = 0;
    cParams = pMethodInfo->paramNum;
    pParams = pMethodInfo->params;

    for (n = 0; n < cParams; n++) {
        if (BT_IS_OUT(pParams[n])) {    // [out]
            uOutSize += sizeof(UInt32); // for pointer

            switch (BT_TYPE(pParams[n])) {
                case BT_TYPE_PUINT8:
                case BT_TYPE_PUINT16:
                case BT_TYPE_PUINT32:
                    uSize = sizeof(UInt32);
                    break;

                case BT_TYPE_PUINT64:
                    uSize = sizeof(UInt64);
                    break;

                case BT_TYPE_PEMUID:
                    uSize = sizeof(EMuid);
                    break;

                case BT_TYPE_PEGUID:
                    uSize = sizeof(EMuid) + sizeof(wchar_t *) \
                            + 80 * sizeof(wchar_t);
                    break;

                case BT_TYPE_PSTRUCT :
                    uSize = sizeof(UInt32) * BT_TYPE_SIZE(pParams[n]);
                    break;

                case BT_TYPE_WSTRINGBUF:
                case BT_TYPE_ASTRINGBUF:
                case BT_TYPE_BUFFEROF:
                case BT_TYPE_ARRAYOF:{
                    if (*puArgs) {
                        uSize = sizeof(CarQuintet) + \
                          MSH_ALIGN_4(((PCARQUINTET)*puArgs)->m_size);
                    }
                    break;
                }

                case BT_TYPE_PINTERFACE:
                    uSize = sizeof(InterfacePack);
                    break;

                default:
                    MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                        "MshProc: Invalid [in, out] type(%08x).\n",
                        pParams[n]));
                    assert(0);
                    return E_INVALID_ARGUMENT;
            }

            if (*puArgs) {
                uOutSize += uSize;
            }
            // else: null pointer do not contain data.
        }

        if (BT_IS_IN(pParams[n])) { // [in]
            switch (BT_TYPE(pParams[n])) {
                case BT_TYPE_UINT8:
                case BT_TYPE_UINT16:
                case BT_TYPE_UINT32:
                    uInSize += sizeof(UInt32);
                    puArgs++;
                    break;

                case BT_TYPE_PUINT8:
                case BT_TYPE_PUINT16:
                case BT_TYPE_PUINT32:
                    if (*puArgs) {
                        uInSize += sizeof(UInt32) * 2;
                    }
                    else { // null pointer
                        uInSize += sizeof(UInt32);
                    }
                    puArgs++;
                    break;

                case BT_TYPE_UINT64:
#ifdef _mips
                    // Adjust for 64bits align on mips
                    if (!(n % 2)) {
                        uInSize += 4;
                        puArgs += 1;
                    }
#endif
                    uInSize += sizeof(UInt64);
                    puArgs += 2;
                    break;

                case BT_TYPE_PUINT64:
                    if (*puArgs) {
                        uInSize += sizeof(UInt32) + sizeof(UInt64);
                    }
                    else { // null pointer
                        uInSize += sizeof(UInt32);
                    }
                    puArgs++;
                    break;

                case BT_TYPE_EGUID:
                    uInSize += sizeof(EMuid) + sizeof(wchar_t *) \
                              + 80 * sizeof(wchar_t);
                    puArgs += sizeof(EMuid) / 4 + sizeof(wchar_t *) / 4;
                    break;

                case BT_TYPE_EMUID:
                    uInSize += sizeof(EMuid);
                    puArgs += sizeof(EMuid) / 4;
                    break;

                case BT_TYPE_PSTRUCT:
                case BT_TYPE_STRUCT:
                    if (*puArgs) {
                        uInSize += sizeof(UInt32) + sizeof(UInt32) * BT_TYPE_SIZE(pParams[n]);
                    }
                    else {  // null pointer
                        uInSize += sizeof(UInt32);
                    }
                    puArgs++;
                    break;

                case BT_TYPE_PEGUID:
                    if (*puArgs) {
                        uInSize += sizeof(UInt32) + sizeof(EMuid)
                                   + 80 * sizeof(wchar_t)
                                   + sizeof(wchar_t *);
                    }
                    else {  // null pointer
                        uInSize += sizeof(UInt32);
                    }
                    puArgs++;
                    break;

                case BT_TYPE_PEMUID:
                    if (*puArgs) {
                        uInSize += sizeof(UInt32) + sizeof(EMuid);
                    }
                    else {  // null pointer
                        uInSize += sizeof(UInt32);
                    }
                    puArgs++;
                    break;

                case BT_TYPE_ASTRING:
                    if (*puArgs) {
                        UInt32 nBufLen = (strlen((char *)(*puArgs)) + 1)
                            * sizeof(char);
                        uInSize += sizeof(UInt32) + sizeof(UInt32) +
                            MSH_ALIGN_4(nBufLen);
                    }
                    else {
                        uInSize += sizeof(UInt32);
                    }
                    puArgs++;
                    break;

                case BT_TYPE_WSTRING:
                    if (*puArgs) {
                        UInt32 nBufLen = (wcslen((wchar_t *)(*puArgs)) + 1)
                            * sizeof(wchar_t);
                        uInSize += sizeof(UInt32) + sizeof(UInt32) +
                            MSH_ALIGN_4(nBufLen);
                    }
                    else {
                        uInSize += sizeof(UInt32);
                    }
                    puArgs++;
                    break;

                case BT_TYPE_INTERFACE:
                    if (*puArgs) {
                        uInSize += sizeof(InterfacePack);
                    }
                    else {  // null pointer
                        uInSize += sizeof(UInt32);
                    }
                    puArgs++;
                    break;

                case BT_TYPE_PINTERFACE:
                    if (*puArgs) {
                        if (*(UInt32 *)(*puArgs)) {
                            uInSize += sizeof(InterfacePack);
                        }
                        else {
                            uInSize += sizeof(UInt32);
                        }
                    }
                    uInSize += sizeof(UInt32);  // for pointer
                    puArgs++;
                    break;

                case BT_TYPE_WSTRINGBUF:
                case BT_TYPE_ASTRINGBUF:
                case BT_TYPE_BUFFEROF:
                case BT_TYPE_ARRAYOF:
                    if (*puArgs) {
                        if (CarQuintetFlag_Type_IObject
                            != (((PCARQUINTET)*puArgs)->m_flags
                            & CarQuintetFlag_TypeMask)) {
                            uInSize += MSH_ALIGN_4(sizeof(UInt32) \
                              + sizeof(CarQuintet) \
                              + ((PCARQUINTET)*puArgs)->m_size);
                        }
                        else {
                            uInSize += MSH_ALIGN_4(sizeof(UInt32) \
                              + sizeof(CarQuintet));
                            int used = ((PCARQUINTET)*puArgs)->m_used
                                        / sizeof(IInterface *);
                            int *pBuf = (int*)((PCARQUINTET)*puArgs)->m_pBuf;
                            uint_t uUsedSize = 0;
                            for (int i = 0; i < used; i++) {
                                if (pBuf[i]) {
                                    uUsedSize += sizeof(InterfacePack);
                                }
                                else {  // null pointer
                                    uUsedSize += sizeof(UInt32);
                                }
                            }
                            uInSize += MAX((MemorySize)uUsedSize, \
                                MSH_ALIGN_4(((PCARQUINTET)*puArgs)->m_size));
                        }
                    }
                    else {  // null pointer
                        uInSize += sizeof(UInt32);
                    }
                    puArgs++;
                    break;

                default:
                    MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                        "MshProc: Invalid [in, out] type(%08x).\n",
                        pParams[n]));
                    assert(0);
                    return E_INVALID_ARGUMENT;
            }
        }
        else {  // [out]
            if (((BT_TYPE(pParams[n]) == BT_TYPE_BUFFEROF) ||
                (BT_TYPE(pParams[n]) == BT_TYPE_ARRAYOF) ||
                (BT_TYPE(pParams[n]) == BT_TYPE_WSTRINGBUF) ||
                (BT_TYPE(pParams[n]) == BT_TYPE_ASTRINGBUF)) && *puArgs) {
                uInSize += sizeof(UInt32); // for size only
            }
            puArgs++;
            uInSize += sizeof(UInt32);
        }
    }

    *puInSize = uInSize;
    *puOutSize = uOutSize;
    return NOERROR;
}

ECode Proxy_ProcessMsh_In(
    /* [in] */ const CIMethodEntry *pMethodInfo,
    /* [in] */ UInt32 *puArgs,
    /* [in, out] */ UInt32 *puBuffer)
{
    int n, cParams;
    const CIBaseType *pParams;
    ECode ec;

    cParams = pMethodInfo->paramNum;
    pParams = pMethodInfo->params;

    for (n = 0; n < cParams; n++) {
        if (BT_IS_IN(pParams[n])) { // [in] or [in, out]
            switch (BT_TYPE(pParams[n])) {
                case BT_TYPE_UINT8:
                case BT_TYPE_UINT16:
                case BT_TYPE_UINT32:
                    *puBuffer = *puArgs;
                    puArgs++;
                    puBuffer++;
                    break;

                case BT_TYPE_PUINT8:
                case BT_TYPE_PUINT16:
                case BT_TYPE_PUINT32:
                    if (*puArgs) {
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;
                        *puBuffer = *(UInt32 *)(*puArgs);
                    }
                    else {  // null pointer
                        *puBuffer = MSH_NULL;
                    }
                    puBuffer++;
                    puArgs++;
                    break;

                case BT_TYPE_UINT64:
#ifdef _mips
                    // Adjust for 64bits align on mips
                    if (!(n % 2)) {
                        puArgs += 1;
                        puBuffer += 1;
                    }
#endif
                    *(UInt64 *)puBuffer = *(UInt64 *)puArgs;
                    puArgs += 2;
                    puBuffer += 2;
                    break;

                case BT_TYPE_PUINT64:
                    if (*puArgs) {
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;
                        *(UInt64 *)puBuffer = *(UInt64 *)(*puArgs);
                        puBuffer += 2;
                    }
                    else {  // null pointer
                        *puBuffer = MSH_NULL;
                        puBuffer++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_STRUCT:
                case BT_TYPE_PSTRUCT:
                    if (*puArgs) {
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;
                        memcpy((PByte)puBuffer, (PByte)(*puArgs), BT_TYPE_SIZE(pParams[n]) * 4);
                        puBuffer += BT_TYPE_SIZE(pParams[n]);
                    }
                    else {  // null pointer
                        *puBuffer = MSH_NULL;
                        puBuffer++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_EGUID:
                    *(EMuid *)puBuffer = *(EMuid *)puArgs;
                    puArgs += sizeof(EMuid) / 4;
                    puBuffer += sizeof(EMuid) / 4;
                    *puBuffer = MSH_NOT_NULL;
                    puBuffer++;
                    memcpy(puBuffer, (void*)*puArgs, 160);
                    puArgs ++;
                    puBuffer += 40;
                    break;

                case BT_TYPE_EMUID:
                    *(EMuid *)puBuffer = *(EMuid *)puArgs;
                    puArgs += sizeof(EMuid) / 4;
                    puBuffer += sizeof(EMuid) / 4;
                    break;

                case BT_TYPE_PEGUID:
                    if (*puArgs) {
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;
                        *(EMuid *)puBuffer = *(EMuid *)(*puArgs);
                        puBuffer += sizeof(EMuid) / 4;
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;
                        memcpy(puBuffer, ((ClassID *)*puArgs)->pUunm, 160);
                        puBuffer += 40;
                    }
                    else {  // null pointer
                        *puBuffer = MSH_NULL;
                        puBuffer++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_PEMUID:
                    if (*puArgs) {
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;
                        *(EMuid *)puBuffer = *(EMuid *)(*puArgs);
                        puBuffer += sizeof(EMuid) / 4;
                    }
                    else {  // null pointer
                        *puBuffer = MSH_NULL;
                        puBuffer++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_WSTRINGBUF:
                case BT_TYPE_ASTRINGBUF:
                case BT_TYPE_BUFFEROF:
                case BT_TYPE_ARRAYOF:
                    if (*puArgs) {
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;
                        memcpy(puBuffer, ((Byte *)*puArgs), \
                                sizeof(CarQuintet)); // copy head
                        puBuffer = (UInt32 *)((Byte *)puBuffer
                                + sizeof(CarQuintet));

                        if (CarQuintetFlag_Type_IObject
                            != (((PCARQUINTET)*puArgs)->m_flags
                            & CarQuintetFlag_TypeMask)) {
                            // copy the storaged data
                            //
                            memcpy(puBuffer, ((PCARQUINTET)*puArgs)->m_pBuf,
                                    ((PCARQUINTET)*puArgs)->m_size);

                            puBuffer = (UInt32 *)((Byte *)puBuffer + \
                                MSH_ALIGN_4(((PCARQUINTET)*puArgs)->m_size));
                        }
                        else {
                            int used = ((PCARQUINTET)*puArgs)->m_used
                                        / sizeof(IInterface *);
                            int *pBuf = (int*)((PCARQUINTET)*puArgs)->m_pBuf;
                            uint_t *puUsedBuf = puBuffer;
                            for (int i = 0; i < used; i++) {
                                if (pBuf[i]) {
                                    ec = StdMarshalInterface(
                                        (IInterface *)pBuf[i],
                                        (InterfacePack *)puUsedBuf);
                                    if (FAILED(ec)) {
                                        MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                                            "MshProc: marshal interface, ec = %x\n", ec));
                                        return ec;
                                    }

                                    puUsedBuf = (UInt32 *)((Byte *)puUsedBuf +
                                                        sizeof(InterfacePack));
                                }
                                else {  // null pointer
                                    *puUsedBuf = 0;
                                    puUsedBuf++;
                                }
                            }
                            puBuffer = (UInt32 *)((Byte *)puBuffer + \
                                MSH_ALIGN_4(((PCARQUINTET)*puArgs)->m_size));
                            puBuffer = (UInt32 *)MAX(puBuffer, puUsedBuf);
                        }
                    }
                    else {  // null pointer
                        *puBuffer = MSH_NULL;
                        puBuffer++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_ASTRING:
                    if (*puArgs) {
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;

                        UInt32 nBufLen = (strlen((char *)(*puArgs)) + 1)
                            * sizeof(char);
                        *puBuffer = nBufLen;
                        puBuffer++;

                        memcpy(puBuffer, (const void *)(*puArgs), nBufLen);
                        puBuffer = (UInt32 *)
                            ((Byte *)puBuffer + MSH_ALIGN_4(nBufLen));
                    }
                    else {  // null pointer
                        *puBuffer = MSH_NULL;
                        puBuffer++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_WSTRING:
                    if (*puArgs) {
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;

                        UInt32 nBufLen = (wcslen((wchar_t *)(*puArgs)) + 1)
                            * sizeof(wchar_t);
                        *puBuffer = nBufLen;
                        puBuffer++;

                        memcpy(puBuffer, (const void *)(*puArgs), nBufLen);
                        puBuffer = (UInt32 *)
                            ((Byte *)puBuffer + MSH_ALIGN_4(nBufLen));
                    }
                    else {  // null pointer
                        *puBuffer = MSH_NULL;
                        puBuffer++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_INTERFACE:
                    if (*puArgs) {
                        ec = StdMarshalInterface(
                            (IInterface *)*puArgs,
                            (InterfacePack *)puBuffer);
                        if (FAILED(ec)) {
                            MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                                "MshProc: marshal interface, ec = %x\n", ec));
                            return ec;
                        }

                        puBuffer = (UInt32 *)((Byte *)puBuffer +
                                            sizeof(InterfacePack));
                    }
                    else {  // null pointer
                        *puBuffer = 0;
                        puBuffer++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_PINTERFACE:
                    if (*puArgs) {
                        *puBuffer = MSH_NOT_NULL;
                        puBuffer++;

                        if (*(UInt32 *)(*puArgs)) {
                            ec = StdMarshalInterface(
                                (IInterface *)*(UInt32 *)*puArgs,
                                (InterfacePack *)puBuffer);
                            if (FAILED(ec)) {
                                MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                                    "MshProc: marshal interface, ec = %x\n",
                                    ec));
                                return ec;
                            }

                            puBuffer = (UInt32 *)((Byte *)puBuffer +
                                                sizeof(InterfacePack));
                        }
                        else {
                            *puBuffer = 0;
                            puBuffer++;
                        }
                    }
                    else {
                        *puBuffer = MSH_NULL;
                        puBuffer++;
                    }
                    puArgs++;
                    break;

                default:
                    MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                        "MshProc: Invalid [in, out] type(%08x).\n",
                        pParams[n]));
                    assert(0);
                    return E_INVALID_ARGUMENT;
            }
        }
        else {  // [out]
            *puBuffer = *puArgs ? MSH_NOT_NULL : MSH_NULL;
            puBuffer++;

            if (((BT_TYPE(pParams[n]) == BT_TYPE_BUFFEROF) ||
                (BT_TYPE(pParams[n]) == BT_TYPE_ARRAYOF) ||
                (BT_TYPE(pParams[n]) == BT_TYPE_WSTRINGBUF) ||
                (BT_TYPE(pParams[n]) == BT_TYPE_ASTRINGBUF)) && *puArgs) {
                *puBuffer = ((PCARQUINTET)*puArgs)->m_size;
                puBuffer++;
            }
            puArgs++;
        }
    }

    return NOERROR;
}

ECode Proxy_ProcessUnmsh_Out(
    /* [in] */ const CIMethodEntry *pMethodInfo,
    /* [in] */ UInt32 *puData,
    /* [in] */ UInt32 uDataSize,
    /* [in, out] */ UInt32 *puArgs)
{
    int n, cParams;
    const CIBaseType *pParams;
    ECode ec;
    cParams = pMethodInfo->paramNum;
    pParams = pMethodInfo->params;

    for (n = 0; n < cParams; n++) {
        if (BT_IS_OUT(pParams[n])) {   // [out] or [in, out]
            if (*puArgs) {
                if (*puData != MSH_NOT_NULL) {
                    MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                        "MshProc: arg conflict in proxy's unmarshal.\n"));
                    return E_INVALID_ARGUMENT;
                }
                puData++;

                switch (BT_TYPE(pParams[n])) {
                    case BT_TYPE_PUINT8:
                        *(UInt8 *)(*puArgs) = *(UInt8 *)puData;
                        puData++;
                        break;

                    case BT_TYPE_PUINT16:
                        *(UInt16 *)(*puArgs) = *(UInt16 *)puData;
                        puData++;
                        break;

                    case BT_TYPE_PUINT32:
                        *(UInt32 *)(*puArgs) = *puData;
                        puData++;
                        break;

                    case BT_TYPE_PUINT64:
                        *(UInt64 *)(*puArgs) = *(UInt64 *)puData;
                        puData += 2;
                        break;

                    case BT_TYPE_PEMUID:
                        *(EMuid *)(*puArgs) = *(EMuid *)puData;
                        puData += sizeof(EMuid) / 4;
                        break;

                    case BT_TYPE_PSTRUCT:
                        memcpy((PByte)(*puArgs), (PByte)puData, BT_TYPE_SIZE(pParams[n]) * 4);
                        puData += BT_TYPE_SIZE(pParams[n]);
                        break;

                    case BT_TYPE_PEGUID:
                        *(EMuid *)(*puArgs) = *(EMuid *)puData;
                        puData += sizeof(EMuid) / 4;
                        memcpy((Void*)*(Address*)(*puArgs + sizeof(EMuid)), \
                            puData + 1, 80 * sizeof(wchar_t));
                        puData += 80 * sizeof(wchar_t) / 4 + 1;
                        break;

                    case BT_TYPE_WSTRINGBUF:
                    case BT_TYPE_ASTRINGBUF:
                    case BT_TYPE_BUFFEROF:
                    case BT_TYPE_ARRAYOF:
                        memcpy(((PCARQUINTET)(UInt32 *)*puArgs)->m_pBuf, \
                            (Byte *)puData + sizeof(CarQuintet),
                            ((PCARQUINTET)puData)->m_used);

                        ((PCARQUINTET)(UInt32 *)*puArgs)->m_used = \
                                ((PCARQUINTET)puData)->m_used;

                        puData = (UInt32 *)((Byte *)puData +
                            MSH_ALIGN_4(sizeof(CarQuintet)\
                                + ((PCARQUINTET)(UInt32 *)*puArgs)->m_size));
                        break;

                    case BT_TYPE_PINTERFACE:
                        if (((InterfacePack *)puData)->m_oid != 0) {
                            ec = StdUnmarshalInterface(
                                (InterfacePack *)puData,
                                CTX_DIFF_PROCESS,
                                FALSE,
                                (IInterface **)*puArgs);
                            if (FAILED(ec)) {
//                                MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
//                                    "MshProc: unmsh interface, ec = %x\n", ec));
                                return ec;
                            }
                        }
                        else {
                            *((IInterface **)*puArgs) = NULL;
                        }

                        puData += sizeof(InterfacePack) / 4;
                        break;

                    default:
                        MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                            "MshProc: Invalid arg type(%08x)\n", pParams[n]));
                        assert(0);
                        return E_INVALID_ARGUMENT;
                }
            }
            else {
                if (*puData != MSH_NULL) {
                    MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                        "MshProc: arg conflict in proxy's unmarshal.\n"));
                    return E_INVALID_ARGUMENT;
                }
                *puData++;
            }
            puArgs++;
        }
        else {      // [in]
            switch (BT_TYPE(pParams[n])) {
                case BT_TYPE_UINT64:
#ifdef _mips
                    // Adjust for 64bits align on mips
                    if (!(n % 2)) {
                        puArgs += 1;
                    }
#endif
                    puArgs += 2;
                    break;

                case BT_TYPE_EMUID:
                    puArgs += sizeof(EMuid) / 4;
                    break;

                case BT_TYPE_EGUID:
                    puArgs += sizeof(EMuid) / 4 + sizeof(wchar_t *) /4;
                    break;

                default:
                    puArgs++;
                    break;
            }
        }
    }

    return NOERROR;
}

ECode Stub_ProcessUnmsh_In(
    /* [in] */ const CIMethodEntry *pMethodInfo,
    /* [in] */ UInt32 *puInData,
    /* [in] */ UInt32 uInSize,
    /* [in, out] */ UInt32 *puOutBuffer,
    /* [in] */ UInt32 uOutSize,
    /* [in, out] */ UInt32 *puArgs)
{
    int n, cParams;
    const CIBaseType *pParams;
    ECode ec;

    if (puOutBuffer) {
        puOutBuffer = (UInt32 *)((MarshalHeader *)puOutBuffer + 1);
        uOutSize -= sizeof(MarshalHeader);
    }
    cParams = pMethodInfo->paramNum;
    pParams = pMethodInfo->params;

    for (n = 0; n < cParams; n++) {
        if (BT_IS_OUT(pParams[n])) {    // [out] or [in, out]
            assert(puOutBuffer);
            if (*puInData == MSH_NOT_NULL) {
                *puOutBuffer = MSH_NOT_NULL;
                puInData++;
                puOutBuffer++;

                switch (BT_TYPE(pParams[n])) { // [out]
                    case BT_TYPE_PUINT8:
                    case BT_TYPE_PUINT16:
                    case BT_TYPE_PUINT32:
                        *puArgs = (UInt32)puOutBuffer;
                        puOutBuffer++;
                        break;

                    case BT_TYPE_PUINT64:
                        *puArgs = (UInt32)puOutBuffer;
                        puOutBuffer += 2;
                        break;

                    case BT_TYPE_PEGUID:
                        *puArgs = (UInt32)puOutBuffer;
                        puOutBuffer += sizeof(EMuid) / 4;
                        *puOutBuffer = (UInt32)(puOutBuffer + 1);
                        puOutBuffer += sizeof(wchar_t *) / 4
                                    + 80 * sizeof(wchar_t) / 4;
                        break;

                    case BT_TYPE_PEMUID:
                        *puArgs = (UInt32)puOutBuffer;
                        puOutBuffer += sizeof(EMuid) / 4;
                        break;

                    case BT_TYPE_PSTRUCT:
                        *puArgs = (UInt32)puOutBuffer;
                        puOutBuffer += BT_TYPE_SIZE(pParams[n]);
                        break;

                    case BT_TYPE_WSTRINGBUF:
                    case BT_TYPE_ASTRINGBUF:
                    case BT_TYPE_BUFFEROF:
                    case BT_TYPE_ARRAYOF:
                        *puArgs = (UInt32)(byte *)puOutBuffer;
                        ((PCARQUINTET)*puArgs)->m_reserve = 0;
                        ((PCARQUINTET)*puArgs)->m_size = *puInData++;
                        ((PCARQUINTET)*puArgs)->m_flags =
                                            CarQuintetFlag_Type_Unknown;
                        ((PCARQUINTET)*puArgs)->m_pBuf = \
                            (byte *)puOutBuffer + sizeof(CarQuintet);
                        if (BT_TYPE(pParams[n]) == BT_TYPE_ASTRINGBUF) {
                            ((PCARQUINTET)*puArgs)->m_used = sizeof(AChar);
                            *(AChar *)(((PCARQUINTET)*puArgs)->m_pBuf) = 0;
                        }
                        else if (BT_TYPE(pParams[n]) == BT_TYPE_WSTRINGBUF) {
                            ((PCARQUINTET)*puArgs)->m_used = sizeof(WChar);
                            *(WChar *)(((PCARQUINTET)*puArgs)->m_pBuf) = 0;
                        }
                        else if (BT_TYPE(pParams[n]) == BT_TYPE_ARRAYOF) {
                            ((PCARQUINTET)*puArgs)->m_used =
                                    ((PCARQUINTET)*puArgs)->m_size;
                        }
                        else {
                            ((PCARQUINTET)*puArgs)->m_used = 0;
                        }
                        puOutBuffer = (UInt32 *)((Byte *)puOutBuffer
                            + MSH_ALIGN_4(sizeof(CarQuintet)
                            + ((PCARQUINTET)*puArgs)->m_size));
                        break;

                    case BT_TYPE_PINTERFACE:
                        *puOutBuffer = 0;
                        *puArgs = (UInt32)puOutBuffer;
                        puOutBuffer += sizeof(InterfacePack) / 4;
                        break;

                    default:
                        MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                            "MshProc: Invalid arg type(%08x) in UnMsh_in.\n",
                            pParams[n]));
                        return E_INVALID_ARGUMENT;
                }
            }
            else if (*puInData == MSH_NULL) {
                *puOutBuffer = MSH_NULL;
                puInData++;
                puOutBuffer++;
                *puArgs = 0;
            }
            else {
                MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                    "MshProc: Invalid pointer value in unmarshal in.\n"));
                return E_INVALID_ARGUMENT;
            }
            puArgs++;
        }
        else if (BT_IS_IN(pParams[n])) {    // [in]
            switch (BT_TYPE(pParams[n])) {
                case BT_TYPE_UINT8:
                case BT_TYPE_UINT16:
                case BT_TYPE_UINT32:
                    *puArgs = *puInData;
                    puArgs++;
                    puInData++;
                    break;

                case BT_TYPE_PUINT8:
                case BT_TYPE_PUINT16:
                case BT_TYPE_PUINT32:
                    if (*puInData != MSH_NULL) {
                        assert(MSH_NOT_NULL == *puInData);
                        puInData++;
                        *puArgs = (UInt32)puInData;
                    }
                    else {
                        *puArgs = 0;
                    }
                    puArgs++;
                    puInData++;
                    break;

                case BT_TYPE_UINT64:
#ifdef _mips
                    // Adjust for 64bits align on mips
                    if (!(n % 2)) {
                        puArgs += 1;
                        puInData += 1;
                    }
#endif
                    *(UInt64 *)puArgs = *(UInt64 *)puInData;
                    puArgs += 2;
                    puInData += 2;
                    break;

                case BT_TYPE_PUINT64:
                    if (*puInData != MSH_NULL) {
                        assert(MSH_NOT_NULL == *puInData);
                        puInData++;
                        *puArgs = (UInt32)puInData;
                        puInData += 2;
                    }
                    else {
                        *puArgs = 0;
                        puInData++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_EGUID:
                    *(EMuid *)puArgs = *(EMuid *)puInData;
                    puArgs += sizeof(EMuid) / 4;
                    puInData += sizeof(EMuid) / 4;
                    *puArgs = (Address)(puInData + 1);
                    puArgs++;
                    *puInData = (UInt32)(puInData + 1);
                    puInData += 41;
                    break;

                case BT_TYPE_EMUID:
                    *(EMuid *)puArgs = *(EMuid *)puInData;
                    puArgs += sizeof(EMuid) / 4;
                    puInData += sizeof(EMuid) / 4;
                    break;

                case BT_TYPE_PEGUID:
                    if (*puInData != MSH_NULL) {
                        assert(MSH_NOT_NULL == *puInData);
                        puInData++;
                        *puArgs = (UInt32)puInData;
                        puInData += sizeof(EMuid) / 4;
                        *puInData = (Address)(puInData + 1);
                        puInData += 41;
                    }
                    else {
                        *puArgs = 0;
                        puInData++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_PEMUID:
                    if (*puInData != MSH_NULL) {
                        assert(MSH_NOT_NULL == *puInData);
                        puInData++;
                        *puArgs = (UInt32)puInData;
                        puInData += sizeof(EMuid) / 4;
                    }
                    else {
                        *puArgs = 0;
                        puInData++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_STRUCT:
                case BT_TYPE_PSTRUCT:
                    if (*puInData != MSH_NULL) {
                        assert(MSH_NOT_NULL == *puInData);
                        puInData++;
                        *puArgs = (UInt32)puInData;
                        puInData += BT_TYPE_SIZE(pParams[n]);
                    }
                    else {
                        *puArgs = 0;
                        puInData++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_WSTRINGBUF:
                case BT_TYPE_ASTRINGBUF:
                case BT_TYPE_BUFFEROF:
                case BT_TYPE_ARRAYOF:
                    if (*puInData != MSH_NULL) {
                        assert(MSH_NOT_NULL == *puInData);
                        puInData++;
                        *puArgs = (UInt32)puInData;
                        if (((PCARQUINTET)((byte *)puInData))->m_size == 0) {
                            ((PCARQUINTET)*puArgs)->m_pBuf = NULL;
                        }
                        else {
                            ((PCARQUINTET)*puArgs)->m_pBuf = \
                                (Byte *)puInData + sizeof(CarQuintet);
                        }
                        puInData = (UInt32 *)((byte *)puInData +
                                    MSH_ALIGN_4(sizeof(CarQuintet)));
                        if (CarQuintetFlag_Type_IObject
                            != (((PCARQUINTET)*puArgs)->m_flags
                            & CarQuintetFlag_TypeMask)) {
                            puInData = (UInt32 *)((byte *)puInData \
                                + MSH_ALIGN_4(((PCARQUINTET)((byte *)puInData \
                                    - sizeof(CarQuintet)))->m_size));
                        }
                        else {
                            int used = ((PCARQUINTET)*puArgs)->m_used
                                        / sizeof(IInterface *);
                            int *pBuf = (int*)((PCARQUINTET)*puArgs)->m_pBuf;
                            uint_t *puUsedInData = puInData;
                            for (int i = 0; i < used; i++) {
                                if (*puUsedInData != 0) {
                                    ec = StdUnmarshalInterface(
                                        (InterfacePack *)puUsedInData,
                                        CTX_DIFF_PROCESS,
                                        FALSE,
                                        (IInterface **)&pBuf[i]);
                                    if (FAILED(ec)) {
                                        MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                                            "MshProc: unmsh interface, ec = %x\n", ec));
                                        return ec;
                                    }

                                    puUsedInData += sizeof(InterfacePack) / 4;
                                }
                                else {  // null pointer
                                    pBuf[i] = 0;
                                    puUsedInData++;
                                }
                            }
                            puInData = (UInt32 *)((byte *)puInData \
                            + MSH_ALIGN_4(((PCARQUINTET)((byte *)puInData \
                                - sizeof(CarQuintet)))->m_size));
                            puInData = (UInt32*)MAX(puInData, puUsedInData);
                        }
                    }
                    else {
                        *puArgs = 0;
                        puInData++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_ASTRING:
                case BT_TYPE_WSTRING:
                    if (*puInData != MSH_NULL) {
                        assert(MSH_NOT_NULL == *puInData);
                        puInData++;

                        UInt32 nBufLen = *puInData;
                        puInData++;

                        *puArgs = (UInt32)puInData;
                        puInData = (UInt32 *)((Byte *)puInData +
                            MSH_ALIGN_4(nBufLen));
                    }
                    else {
                        *puArgs = 0;
                        puInData++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_INTERFACE:
                    if (*puInData != 0) {
                        ec = StdUnmarshalInterface(
                            (InterfacePack *)puInData,
                            CTX_DIFF_PROCESS,
                            FALSE,
                            (IInterface **)puArgs);
                        if (FAILED(ec)) {
                            MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                                "MshProc: unmsh interface, ec = %x\n", ec));
                            return ec;
                        }

                        puInData += sizeof(InterfacePack) / 4;
                    }
                    else {
                        *puArgs = 0;
                        puInData++;
                    }
                    puArgs++;
                    break;

                case BT_TYPE_PINTERFACE:
                    if (*puInData != MSH_NULL) {
                        assert(MSH_NOT_NULL == *puInData);
                        puInData++;
                        if (*puInData != 0) {
                            ec = StdUnmarshalInterface(
                                (InterfacePack *)puInData,
                                CTX_DIFF_PROCESS,
                                FALSE,
                                (IInterface **)puInData);
                            if (FAILED(ec)) {
                                MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                                    "MshProc: unmsh interface, ec = %x\n",
                                    ec));
                                return ec;
                            }
                            *puArgs = (UInt32)puInData;

                            puInData += sizeof(InterfacePack) / 4;
                        }
                        else {
                            *puInData = 0;
                            *puArgs = (UInt32)puInData;
                            puInData++;
                        }
                    }
                    else {
                        *puArgs = 0;
                        puInData++;
                    }
                    puArgs++;
                    break;

                default:
                    MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                        "MshProc: Invalid arg type(%08x)\n", pParams[n]));
                    return E_INVALID_ARGUMENT;
            }
        }
        else {
            MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                "MshProc: Invalid arg type(%08x)\n", pParams[n]));
            return E_INVALID_ARGUMENT;
        }
    }

    return NOERROR;
}

ECode Stub_ProcessMsh_Out(
    /* [in] */ const CIMethodEntry *pMethodInfo,
    /* [in] */ UInt32 *puArgs,
    /* [in, out] */ UInt32 *puOutBuffer,
    /* [in] */ bool_t bOnlyReleaseIn)
{
    int n, cParams;
    const CIBaseType *pParams;
    MarshalHeader * pOutHeader = NULL;
    ECode ec;

    cParams = pMethodInfo->paramNum;
    pParams = pMethodInfo->params;

    // skip the Out Marshal Header;
    if (puOutBuffer) {
        pOutHeader = (MarshalHeader *)(Void *)puOutBuffer;
        puOutBuffer = (UInt32 *)((MarshalHeader *)puOutBuffer + 1);
    }

    for (n = 0; n < cParams; n++) {
        if (BT_IS_OUT(pParams[n])) {    // [out] or [in, out]
            puArgs++;
            if (*puOutBuffer != MSH_NULL) {
                assert(*puOutBuffer == MSH_NOT_NULL);
                puOutBuffer++;
                switch (BT_TYPE(pParams[n])) {
                    case BT_TYPE_PUINT64:
                        puOutBuffer += 2;
                        break;

                    case BT_TYPE_WSTRINGBUF:
                    case BT_TYPE_ASTRINGBUF:
                    case BT_TYPE_BUFFEROF:
                    case BT_TYPE_ARRAYOF:
                        puOutBuffer = (UInt32 *)((Byte *)puOutBuffer +
                            MSH_ALIGN_4(sizeof(CarQuintet)
                            + ((PCARQUINTET)((Byte *)puOutBuffer))->m_size));
                        break;

                    case BT_TYPE_PEMUID:
                        puOutBuffer += sizeof(EMuid) / 4;
                        break;

                    case BT_TYPE_PSTRUCT:
                        puOutBuffer += BT_TYPE_SIZE(pParams[n]);
                        break;

                    case BT_TYPE_PEGUID:
                        puOutBuffer += sizeof(EMuid) / 4 + sizeof(wchar_t *) / 4 \
                                       + 80 * sizeof(wchar_t) / 4;
                        break;

                    case BT_TYPE_PINTERFACE:
                        if (!bOnlyReleaseIn) {
                            IInterface *pObj;

                            pObj = (IInterface *)*puOutBuffer;
                            if (NULL != (IInterface *)*puOutBuffer) {
                                ec = StdMarshalInterface(
                                    pObj, (InterfacePack *)puOutBuffer);
                                pObj->Release();
                                if (FAILED(ec)) {
                                    MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                                  "MshProc: marshal interface, ec = %x\n", ec));
                                    return ec;
                                }
                            }
                            else {
                                ((InterfacePack *)puOutBuffer)->m_oid = 0;
                            }

                            puOutBuffer += sizeof(InterfacePack) / 4;
                        }
                        break;

                    default:
                        puOutBuffer++;
                        break;
                }
            }
            else {
                assert(*puOutBuffer == MSH_NULL);
                puOutBuffer++;
            }
        }
        else {  // BT_IS_IN
            switch (BT_TYPE(pParams[n])) {
                case BT_TYPE_UINT64:
#ifdef _mips
                    // Adjust for 64bits align on mips
                    if (!(n % 2)) {
                        puArgs += 1;
                    }
#endif
                    puArgs += 2;
                    break;

                case BT_TYPE_EMUID:
                    puArgs += sizeof(EMuid) / 4;
                    break;

                case BT_TYPE_EGUID:
                    puArgs += sizeof(EMuid) / 4 + sizeof(wchar_t *) / 4;
                    break;

                case BT_TYPE_PINTERFACE:
                    if (*puArgs && *(UInt32 *)(*puArgs)) {
                        ((IInterface *)*(UInt32 *)*puArgs)->Release();
                    }
                    *puArgs++;
                    break;

                case BT_TYPE_INTERFACE:
                    if (*puArgs) {
                        ((IInterface *)*puArgs)->Release();
                    }
                    *puArgs++;
                    break;

                case BT_TYPE_WSTRINGBUF:
                case BT_TYPE_ASTRINGBUF:
                case BT_TYPE_BUFFEROF:
                case BT_TYPE_ARRAYOF:
                    if (*puArgs) {
                        if (CarQuintetFlag_Type_IObject
                            == (((PCARQUINTET)*puArgs)->m_flags
                            & CarQuintetFlag_TypeMask)) {
                            int used = ((PCARQUINTET)*puArgs)->m_used
                                        / sizeof(IInterface *);
                            int *pBuf = (int*)((PCARQUINTET)*puArgs)->m_pBuf;
                            for (int i = 0; i < used; i++) {
                                if (pBuf[i]) {
                                    ((IInterface *)pBuf[i])->Release();
                                }
                            }
                        }
                    }
                    *puArgs++;
                    break;

                default:
                    *puArgs++;
                    break;
            }
        }
    }

    if (pOutHeader) {
        pOutHeader->m_uOutSize = (UInt32)puOutBuffer - (UInt32)pOutHeader;
    }

    return NOERROR;
}

Void ProxyUnmarshalOutErrorInfo(
    /* [in] */ MarshalHeader * pHeader)
{
    assert(pHeader);
    assert(pHeader->m_uOutSize <= sizeof(MarshalHeader) + ERROR_INFO_SIZE);

    ErrorInfoHeader * pErrorInfo = (ErrorInfoHeader *)(Void *)(pHeader + 1);

    assert(pErrorInfo->m_uSize > sizeof(ErrorInfoHeader)
           && pErrorInfo->m_uSize <= ERROR_INFO_SIZE);

//    Void * pStack = (Void *)((UInt32)_EzGetErrorInfo() - sizeof(ErrorInfoHeader));
    Void * pStack = (Void *)((UInt32)GetErrorInfo() - sizeof(ErrorInfoHeader));

    memcpy(pStack, pErrorInfo, pErrorInfo->m_uSize);
}

Void StubMarshalOutErrorInfo(
    /* [out] */ MarshalHeader * pOutHeader)
{
    assert(pOutHeader);

   // ErrorInfoHeader * pErrorInfo = (ErrorInfoHeader *)(Void *)
   //                         ((UInt32)_EzGetErrorInfo() - sizeof(ErrorInfoHeader));

   ErrorInfoHeader * pErrorInfo = (ErrorInfoHeader *)(Void *)
                            ((UInt32)GetErrorInfo() - sizeof(ErrorInfoHeader));

    assert(pErrorInfo->m_uSize > sizeof(ErrorInfoHeader)
           && pErrorInfo->m_uSize <= ERROR_INFO_SIZE);

    pOutHeader->m_uOutSize = sizeof(MarshalHeader) + pErrorInfo->m_uSize;

    memcpy(pOutHeader + 1, pErrorInfo, pErrorInfo->m_uSize);
}
